`timescale 1 ns / 1 ps 
/**
 ******************************************************************************
 * @file    jtag_process.v
 * @author  KEN
 * @version V1.2
 * @date    Jul. 6th, 2020
 * @brief   Convert JTAG vectors to signals
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2020 K'sP</center></h2>
 ******************************************************************************
 */

module jtag_process
	   #
	   (
		   parameter integer C_JTAG_TYPE = 0, //0=normal, 1=with tri io control
		   parameter integer C_JTAG_TRI_POLARITY = 0, //0=<low as input, high as output> 1=<high as input, low as output>
		   
		   parameter integer C_VECTOR_LEN = 128
	   )
	   (
		   (* X_INTERFACE_INFO = "xilinx.com:signal:clock:1.0 m_jtag_aclk CLK" *)
		   (* X_INTERFACE_PARAMETER = "ASSOCIATED_BUSIF m_jtag, ASSOCIATED_RESET m_jtag_aresetn" *)
		   input wire m_jtag_aclk,

		   (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 m_jtag_aresetn RST" *)
		   (* X_INTERFACE_PARAMETER = "POLARITY ACTIVE_LOW" *)
		   input wire m_jtag_aresetn,

		   input wire ENABLE,
		   output wire BUSY,

		   input wire [31 : 0] LENGTH,
		   input wire [31 : 0] FREQ_WORD,

		   input wire [(C_VECTOR_LEN - 1) : 0] TMS_VECTOR,
		   input wire [(C_VECTOR_LEN - 1) : 0] TDI_VECTOR,
		   output wire [(C_VECTOR_LEN - 1) : 0] TDO_VECTOR,

		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TCK" *)
		   output wire m_tck,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TMS" *)
		   output wire m_tms,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TDI" *)
		   output wire m_tdi,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TDO" *)
		   input wire m_tdo,

		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TCK_IO" *)
		   inout wire m_tck_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TCK_T" *)
		   output wire m_tck_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TMS_IO" *)
		   inout wire m_tms_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TMS_T" *)
		   output wire m_tms_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDI_IO" *)
		   inout wire m_tdi_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDI_T" *)
		   output wire m_tdi_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDO_IO" *)
		   inout wire m_tdo_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDO_T" *)
		   output wire m_tdo_t
	   );

wire clk_divided;

clk_div #
	(
		.C_FREQ_WORD_WIDTH(32)
	)
	clk_div_inst
	(
		.aclk(m_jtag_aclk),
		.aresetn(m_jtag_aresetn),
		.FREQ_WORD(FREQ_WORD),
		.clk_out(clk_divided)
	);

reg busy_r;
reg [(C_VECTOR_LEN - 1) : 0] tdo_vector_r;

assign BUSY = busy_r;
assign TDO_VECTOR = tdo_vector_r;

reg tck_r;
reg tms_r;
reg tdi_r;
wire tdo_w;

localparam STATE_1_IDLE = 'd0;
localparam STATE_1_START = 'd1;
localparam STATE_1_EDGE_DETECT = 'd2;
reg [1: 0] state_1;

localparam STATE_2_TCK_L = 'd0; //tck latch edge
localparam STATE_2_TCK_S = 'd1; //tck sample edge
localparam STATE_2_CHECK = 'd2; //byte index check
localparam STATE_2_END = 'd3;
reg [1: 0] state_2;

reg [31: 0] bit_index;

reg clk_divided_latch;

always@(posedge m_jtag_aclk or negedge m_jtag_aresetn)
begin
	if (!m_jtag_aresetn)
	begin
		busy_r <= 1'b0;
		tdo_vector_r <= 0;

		tck_r <= 1'b0;
		tms_r <= 1'b0;
		tdi_r <= 1'b0;

		state_1 <= STATE_1_IDLE;
		state_2 <= STATE_2_TCK_L;
		bit_index <= 32'd0;

		clk_divided_latch <= 1'b0;
	end
	else
	begin
		clk_divided_latch <= clk_divided;

		case (state_1)
			STATE_1_IDLE:
			begin
				if (ENABLE == 1'b0)
				begin
					state_1 <= STATE_1_START;
				end
			end

			STATE_1_START:
			begin
				//ENABLE Rise Edge
				if ((ENABLE == 1'b1) && (LENGTH > 0) && (LENGTH <= C_VECTOR_LEN))
				begin
					busy_r <= 1'b1;
					tdo_vector_r <= 0;

					tck_r <= 1'b0;
					tms_r <= 1'b0;
					tdi_r <= 1'b0;

					state_1 <= STATE_1_EDGE_DETECT;
					bit_index <= 32'd0;
				end
			end

			STATE_1_EDGE_DETECT:
			begin
				if ((clk_divided_latch != clk_divided) && (clk_divided == 1))
				begin
					case (state_2)
						STATE_2_TCK_L:
						begin
							tck_r <= 1'b0;
							tms_r <= TMS_VECTOR[bit_index];
							tdi_r <= TDI_VECTOR[bit_index];

							state_2 <= STATE_2_TCK_S;
						end

						STATE_2_TCK_S:
						begin
							tck_r <= 1'b1;
							tdo_vector_r[bit_index] <= tdo_w;

							bit_index <= bit_index + 32'd1;

							state_2 <= STATE_2_CHECK;
						end

						STATE_2_END:
						begin
							tck_r <= 1'b0;
							tms_r <= 1'b0;
							tdi_r <= 1'b0;

							busy_r <= 1'b0;

							state_1 <= STATE_1_IDLE;
							state_2 <= STATE_2_TCK_L;
						end

					endcase
				end
				else
				begin
					case (state_2)
						STATE_2_CHECK:
						begin
							if (bit_index >= LENGTH)
							begin
								state_2 <= STATE_2_END;
							end
							else
							begin
								state_2 <= STATE_2_TCK_L;
							end
						end

					endcase
				end
			end

			default:
			begin
				state_1 <= STATE_1_IDLE;
				state_2 <= STATE_2_TCK_L;
			end

		endcase
	end
end

generate

	if (C_JTAG_TYPE == 0)
	begin : io_none_ctl
		assign m_tck = tck_r;
		assign m_tms = tms_r;
		assign m_tdi = tdi_r;
		assign tdo_w = m_tdo;
	end
	else
	begin : io_dir_ctl

		IOBUF IOBUF_tck (
				  .IO(m_tck_io),
				  .I(tck_r),
				  .T(BUSY ? 1'b0 : 1'b1) // 3-state enable input, high=input, low=output
			  );
		assign m_tck_t = BUSY ? (~C_JTAG_TRI_POLARITY) : C_JTAG_TRI_POLARITY; //low=input, high=output

		IOBUF IOBUF_tms (
				  .IO(m_tms_io),
				  .I(tms_r),
				  .T(BUSY ? 1'b0 : 1'b1) // 3-state enable input, high=input, low=output
			  );
		assign m_tms_t = BUSY ? (~C_JTAG_TRI_POLARITY) : C_JTAG_TRI_POLARITY; //low=input, high=output

		IOBUF IOBUF_tdi (
				  .IO(m_tdi_io),
				  .I(tdi_r),
				  .T(BUSY ? 1'b0 : 1'b1) // 3-state enable input, high=input, low=output
			  );
		assign m_tdi_t = BUSY ? (~C_JTAG_TRI_POLARITY) : C_JTAG_TRI_POLARITY; //low=input, high=output

		IOBUF IOBUF_tdo (
				  .IO(m_tdo_io),
				  .O(tdo_w),
				  .T(1'b1) // 3-state enable input, high=input, low=output
			  );
		assign m_tdo_t = C_JTAG_TRI_POLARITY; //low=input, high=output
	end

endgenerate

endmodule